package com.eden.door.parsers;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.eden.door.constant.Constant_Sys;
import com.eden.door.constant.Constants;
import com.eden.door.entity.Door;
import com.eden.door.entity.DoorAttr;
import com.eden.door.entity.Formula;
import com.eden.door.entity.FormulaJavaFile;

public class FormulaParser {
	private final static Log LOG = LogFactory.getLog(FormulaParser.class);

	@SuppressWarnings("unchecked")
	public List<? extends Formula> parse(File doorXmlFile , Door door){
		List formulaList = new ArrayList() ;
		
		List<FormulaJavaFile> formulaJavaFileList = convert2Java(doorXmlFile, door) ;
		
		try{
			JavaCompiler compiler = ToolProvider.getSystemJavaCompiler() ;
			URL formulaClasspath = null ;
			URLClassLoader classLoader = null ;
			Class<?> formulaImplClass = null ;
			for(FormulaJavaFile formulaJavaFile : formulaJavaFileList) {
				if(LOG.isDebugEnabled())
					LOG.debug("编译加载公 式类:" + formulaJavaFile.getFile().getPath());
				compiler.run(null, null, null, "-encoding", "UTF-8","-d",Constants.COMPILE_PATH ,formulaJavaFile.getFile().getPath()) ;
				formulaClasspath = new URL(Constants.CLASSLOAD_URL) ;
				classLoader = new URLClassLoader(new URL[]{formulaClasspath}) ;
				
				formulaImplClass = classLoader.loadClass(Constants.DYNAMIC_CODE_PACKAGE +"."+ formulaJavaFile.getFileName()) ;
				formulaList.add(((Formula)formulaImplClass.newInstance())) ;
			}
		} catch (Exception e) {
			LOG.error("编译加载公 式类出错!" , e) ;
		}
		
		return formulaList ;
	}
	/**
	 * 把配置文件中<formulas>标签中的计算公式转换为java源文件
	 * @param doorXmlFile　门的配置文件
	 * @param door　已经解析出属性的类
	 * @return　返回计算式源文件的List
	 */
	public List<FormulaJavaFile> convert2Java(File doorXmlFile, Door door) {
		List<FormulaJavaFile> list = new ArrayList<FormulaJavaFile>();

		

		SAXReader reader = new SAXReader();
		Document document = null;

		try {
			if (LOG.isDebugEnabled())
				LOG.debug("读取门配置文件,解析运算公式");
			document = reader.read(doorXmlFile);
		} catch (DocumentException e) {
			e.printStackTrace();
			LOG.error("读取门配置文件出错") ;
		}
		Element root = document.getRootElement();
		Element formulas = root.element("formulas");
		Element formula = formulas.element("formula");

		//循环每个formula标签，生成一个java源文件
		int index = 0; //计算公式的顺序
		for (@SuppressWarnings("unchecked")
		Iterator<Element> formulasIterator = formulas.elementIterator(); formulasIterator
				.hasNext();) {
			formula = formulasIterator.next();
			if (StringUtils.equals(formula.getName(), "formula")) {
				list.add(formulaTagHandler(door , index++ , formula)) ;

			}
		}

		return list;
	}

	/**
	 * 处理配置文件中的<formula>标签  , 生成一个java源文件
	 * @param door 该运算式所属的门
	 * @param index 运算公式的顺序
	 * @param formula 一个dom4j的formula标签
	 * @return 返回一个FormulaJavaFile类
	 */
	public FormulaJavaFile formulaTagHandler(Door door, int index,
			Element formula) {
		StringBuilder sbSrc = null;
		File doorJavaFile = null;
		
		//源文件名字的命名规则：是Formula类的子类加门的编码和计算公式的顺序
		String fileName = "FormulaImpl" + door.getCode() + index;
		doorJavaFile = new File(Constants.DYNAMIC_CODE_PATH + fileName
				+ ".java");
		
		sbSrc = new StringBuilder();

		// 包的位置
		sbSrc.append("package " + Constants.DYNAMIC_CODE_PACKAGE + ";");
		// 引入包
		sbSrc.append("import com.eden.door.entity.DoorAttr;");
		sbSrc.append("import com.eden.door.entity.Formula ;");
		sbSrc.append("import java.util.Map ;");
		sbSrc.append("import java.util.Iterator ;");
		sbSrc.append("import java.util.Map.Entry ;");
		sbSrc.append("import java.util.Map.Entry ;import com.eden.door.util.Calculator;");

		sbSrc.append("public class ");
		sbSrc.append(fileName);
		sbSrc.append(" extends Formula {");

		// 所有门属性的变量
		Map<String, DoorAttr> doorAttrs = door.getDoorAttrs();
		String doorAttrValue = "0";
		for (Iterator<Entry<String, DoorAttr>> i = doorAttrs.entrySet()
				.iterator(); i.hasNext();) {
			DoorAttr doorAttr = i.next().getValue();

			sbSrc.append(" private String ");
			sbSrc.append(doorAttr.getCode());
			
			// 如果value为空字符串默认设为0
//			doorAttrValue = StringUtils.isBlank(doorAttr.getValue()) ? "0"
//					: doorAttr.getValue();
			sbSrc.append(" = \"" + doorAttr.getValue() + "\" ; ");
		}

		// 实现Formula的calculate方法
		sbSrc.append("@Override ");
		sbSrc.append("public Map<String ,DoorAttr> calculate(Map<String , DoorAttr> doorAttrMap) {");
		
		
		//********************************这个阶段是在生成源文件时，
		
		//计算之前把计算后的值赋给现在运算式中的变量 
		for (Iterator<Entry<String, DoorAttr>> entryIterator = doorAttrs
				.entrySet().iterator(); entryIterator.hasNext();) {
			DoorAttr doorAttr = entryIterator.next().getValue();
			
			if(StringUtils.equals(doorAttr.getFill() , Constant_Sys.DOOR_ATTR_FILL_AUTO) ){
				sbSrc.append(" this." + doorAttr.getCode() + "= getValueByCode(\"" + doorAttr.getCode() + "\" , doorAttrMap ) ; " );
			}
		}
		
		@SuppressWarnings("unchecked")
		Iterator<Element> i = formula.elementIterator();
		
		// 如果没有判断语句，就直接写入运算表达式
		if (!i.hasNext()) {
			sbSrc.append(this.tokenHandler(formula.getData().toString()) + " ; ");
		}
		//如果有判断语句，转换
		for (; i.hasNext();) {
			Element e = i.next();
			if (e.getName().equals("if")) {
				sbSrc.append(" if(" + attrTokenHandler(e.attributeValue("test")) + "){");
				sbSrc.append(this.tokenHandler(e.getData().toString()) + ";");
				sbSrc.append(" } ");
			} else if (StringUtils.equals(e.getName(), "elseif")) {
				sbSrc.append(" else if(" + attrTokenHandler(e.attributeValue("test")) + "){");
				sbSrc.append(this.tokenHandler(e.getData().toString()) + ";");
				sbSrc.append(" } ");
			} else if (StringUtils.equals(e.getName(), "else")) {
				sbSrc.append(" else {");
				sbSrc.append(this.tokenHandler(e.getData().toString()) + ";");
				sbSrc.append(" } ");
			}
		}
		sbSrc.append("Map<String ,DoorAttr> rs = doorAttrMap ;");
		sbSrc.append("for(Iterator<Entry<String , DoorAttr>> i = doorAttrMap.entrySet().iterator() ; i.hasNext();){ ");
		sbSrc.append(" DoorAttr doorAttr = i.next().getValue() ; ");
		for (Iterator<Entry<String, DoorAttr>> entryIterator = doorAttrs
				.entrySet().iterator(); entryIterator.hasNext();) {
			DoorAttr doorAttr = entryIterator.next().getValue();

			// 如果此属性为fill属性为auto刚是由计算机计算并设置到属性集合中
			if (StringUtils.equals("auto", doorAttr.getFill())) {
				sbSrc.append(" if(doorAttr.getCode().equals(\"" + doorAttr.getCode() + "\")){");
				sbSrc.append(" doorAttr.setValue(String.valueOf(" + doorAttr.getCode()
						+ ")) ;");
				sbSrc.append(" rs.put( \"" + doorAttr.getCode() +"\",doorAttr); ");
				sbSrc.append(" } ");
			}

		}
		sbSrc.append(" } ");
		sbSrc.append("return rs ; ");
		sbSrc.append("}");
		sbSrc.append("}");

		
		//System.out.println(doorJavaFile.getAbsolutePath());
		//如果文件不存在，创建新的java文件
		if (!doorJavaFile.exists()) {
			try {
				doorJavaFile.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		Writer writer = null;
		try {
			if(LOG.isDebugEnabled())
				LOG.debug("开始写入java源文件" + doorJavaFile.getPath()) ;
			writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(doorJavaFile) , "utf-8"));

			writer.write(sbSrc.toString());
			writer.flush();
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (writer != null) {
				try {
					writer.close();
				} catch (IOException e) {
					// ignore
				}
			}
		}
		
		return new FormulaJavaFile(index , fileName , doorJavaFile );
	}
	
	/**
	 * 处理公式中的对属性的引用时的符号
	 * @param data
	 * @return
	 */
	public String tokenHandler(String data){
		data = data.trim().replaceFirst("\\$\\{", "") ;
		data = data.replaceFirst("\\}", "") ;
		
		data = data.replace("${", "Double.valueOf(getValueByCode(\"") ;
		data = data.replace("}" , "\", doorAttrMap))") ;
		data = data.replace("=" , " = String.valueOf(" ) ;
		data += ")" ;
		return data;
	}
	/**
	 * 处理条件中的test
	 * @param data
	 * @return
	 */
	public String attrTokenHandler(String data){
		data = data.trim().replace("${", "Integer.valueOf(getValueByCode(\"") ;
		data = data.replace("}" , "\" , doorAttrMap))") ;
		return data;
	}
}
